package org.hepeng.workx.mybatis.spring.boot.autoconfigure;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.hepeng.workx.extension.XLoader;
import org.hepeng.workx.mybatis.MybatisConstant;
import org.hepeng.workx.mybatis.spring.WorkXSqlSessionFactoryBean;
import org.joor.Reflect;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.util.List;
import java.util.Objects;
import java.util.Properties;

/**
 * @author he peng
 */

@ConditionalOnClass({
        SqlSessionFactory.class, SqlSessionFactoryBean.class ,
        WorkXSqlSessionFactoryBean.class ,WorkXMybatisProperties.class})
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(WorkXMybatisProperties.class)
@AutoConfigureAfter({DataSourceAutoConfiguration.class})
@ConditionalOnProperty(
        prefix = WorkXMybatisProperties.WORKX_MYBATIS_PREFIX ,
        name = "enable" ,
        havingValue = "true")
public class WorkXMybatisAutoConfiguration {

    private WorkXMybatisProperties properties;
    private MybatisAutoConfiguration mybatisAutoConfiguration;
    private MybatisProperties mybatisProperties;
    private Reflect mybatisAutoConfigurationReflect;

    public WorkXMybatisAutoConfiguration(WorkXMybatisProperties properties , MybatisAutoConfiguration mybatisAutoConfiguration) {
        this.properties = properties;
        this.mybatisAutoConfiguration = mybatisAutoConfiguration;
        this.mybatisAutoConfigurationReflect = Reflect.on(this.mybatisAutoConfiguration);
        this.mybatisProperties = this.mybatisAutoConfigurationReflect.get("properties");
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource , ApplicationContext context) throws Exception {
        SqlSessionFactoryBean factory = XLoader.getXLoader(SqlSessionFactoryBean.class , MybatisConstant.MYBATIS_X_POINT_DIRECTORY).getX();

        if (WorkXSqlSessionFactoryBean.class.isAssignableFrom(factory.getClass())) {
            WorkXSqlSessionFactoryBean workXFactory = (WorkXSqlSessionFactoryBean) factory;
            workXFactory.setApplicationContext(context);
            workXFactory.setEnableDataSourceRoute(this.properties.getEnableDataSourceRoute());
            workXFactory.setEnablePublishEvent(this.properties.getEnablePublishEvent());
            workXFactory.setEventConsumerPackages(this.properties.getEventConsumerPackages());
            workXFactory.setDialect(this.properties.getDialect());
        }

        factory.setDataSource(dataSource);
        factory.setVfs(SpringBootVFS.class);
        if (StringUtils.hasText(this.mybatisProperties.getConfigLocation())) {
            ResourceLoader resourceLoader = mybatisAutoConfigurationReflect.get("resourceLoader");
            factory.setConfigLocation(resourceLoader.getResource(this.mybatisProperties.getConfigLocation()));
        }
        Configuration configuration = this.mybatisProperties.getConfiguration();
        if (configuration == null && !StringUtils.hasText(this.mybatisProperties.getConfigLocation())) {
            configuration = XLoader.getXLoader(Configuration.class , MybatisConstant.MYBATIS_X_POINT_DIRECTORY).getX();
        }

        if (Objects.nonNull(configuration)) {
            Configuration configurationX = XLoader.getXLoader(Configuration.class , MybatisConstant.MYBATIS_X_POINT_DIRECTORY).getX();
            BeanUtils.copyProperties(configurationX , configuration);
            configuration = configurationX;
            this.mybatisProperties.setConfiguration(configuration);
        }

        List<ConfigurationCustomizer> configurationCustomizers =
                mybatisAutoConfigurationReflect.get("configurationCustomizers");
        if (configuration != null && !CollectionUtils.isEmpty(configurationCustomizers)) {
            for (ConfigurationCustomizer customizer : configurationCustomizers) {
                customizer.customize(configuration);
            }
        }

        Properties variables = new Properties();
        variables.setProperty(MybatisConstant.ENABLE_DATASOURCE_ROUTE , Boolean.toString(this.properties.getEnableDataSourceRoute()));
        variables.setProperty(MybatisConstant.ENABLE_PUBLISH_EVENT , Boolean.toString(this.properties.getEnablePublishEvent()));
        if (org.apache.commons.lang3.StringUtils.isNotBlank(this.properties.getEventConsumerPackages())) {
            variables.setProperty(MybatisConstant.EVENT_CONSUMER_PACKAGES , this.properties.getEventConsumerPackages());
        }

        factory.setConfiguration(configuration);
        if (this.mybatisProperties.getConfigurationProperties() != null) {
            variables.putAll(this.mybatisProperties.getConfigurationProperties());
        }

        factory.setConfigurationProperties(variables);


        Interceptor[] interceptors = mybatisAutoConfigurationReflect.get("interceptors");
        if (!ObjectUtils.isEmpty(interceptors)) {
            factory.setPlugins(interceptors);
        }

        DatabaseIdProvider databaseIdProvider = mybatisAutoConfigurationReflect.get("databaseIdProvider");
        if (databaseIdProvider != null) {
            factory.setDatabaseIdProvider(databaseIdProvider);
        }

        if (StringUtils.hasLength(this.mybatisProperties.getTypeAliasesPackage())) {
            factory.setTypeAliasesPackage(this.mybatisProperties.getTypeAliasesPackage());
        }

        if (StringUtils.hasLength(this.mybatisProperties.getTypeHandlersPackage())) {
            factory.setTypeHandlersPackage(this.mybatisProperties.getTypeHandlersPackage());
        }

        if (!ObjectUtils.isEmpty(this.mybatisProperties.resolveMapperLocations())) {
            factory.setMapperLocations(this.mybatisProperties.resolveMapperLocations());
        }

        return factory.getObject();
    }
}
